package cc.verywell.pureblock.func.node;

import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;

import cc.verywell.pureblock.func.FuncProvider;
import cc.verywell.pureblock.func.UnsignedNodeException;
import cc.verywell.pureblock.log.Pblog;
import cc.verywell.pureblock.net.Msg;
import cc.verywell.pureblock.net.NormalSession;
import cc.verywell.pureblock.util.StringUtil;
import cc.verywell.pureblock.util.sign.RsaClient;

/**
* 
* 报文:
* 身份公钥长度[2_bits] 身份公钥[?_bits] 
* 功能数量[2_bits] {功能字符长度[1_bit] 功能描叙字符[?_bits]} 
* 对外地址数量[1_bit] 对外地址长度[2_bits] 对外地址[?_bits]  
* 发送时间[8_bit]
* 盐[4_bit]
* 签名长度[2_bits] 签名[?_bits] 
*
* Copyright 2019 TanYaqiu
* @author TanYaqiu 
* @address flat 601
* @date 2019年3月11日 下午10:35:50
*/
public class Node implements Msg{
	
	protected byte[] pubKey;
	private String[] receiveFuncs;
	private String[] remote;
	final private int salt;
	private long remoteSentTime;
	final private byte[] sign;

	protected Node() {sign=null;salt=0;}
	public Node(ByteBuffer bb) {
		int start = bb.position();
		pubKey = new byte[0xFFFF & bb.getShort()];
		bb.get(pubKey);
		receiveFuncs = new String[0xFFFF & bb.getShort()];
		for (int i = 0; i < receiveFuncs.length; i++) {
			receiveFuncs[i] = StringUtil.byteBufferToString(1, bb);
		}
		remote = new String[0xFF & bb.get()];
		for (int i = 0; i < remote.length; i++) {
			remote[i] = StringUtil.byteBufferToString(2, bb);
		}
		salt = bb.getInt();
		remoteSentTime = bb.getLong();
		int position = bb.position();
		sign = new byte[0xFFFF & bb.getShort()];
		bb.get(sign);
		int end = bb.position();
		int limit = bb.limit();
		boolean sd = false;
		try {
			RsaClient rsa = new RsaClient(pubKey);
			bb.limit(position).position(start);
			sd = rsa.check(bb, sign);
		} catch (NoSuchAlgorithmException | InvalidKeySpecException | SignatureException e) {
			if(Pblog.SECURE)Pblog.SECURE(e);
		}
		if(!sd) {
			throw new UnsignedNodeException(Pblog.byteArray2Hex(sign)); 
		}
		bb.limit(limit).position(end);
	}
	
	/**
	 * 为了兼容继承类;
	 */
	@Override
	public void write(ByteBuffer bb, NormalSession nc) {
//		bb.put(msghead);
//		bb.put((byte)1); //MSG的正式编号;
		byte[] pubKey = this.getPubKey();
		if(pubKey==null) {
			bb.putShort((short)0);
		}else {
			bb.putShort((short)pubKey.length);
			bb.put(pubKey);
		}
		String[] receiveFuncs = this.getFuncs();
		if(receiveFuncs==null) {
			bb.putShort((short)0);
		}else {
			bb.putShort((short)receiveFuncs.length);
			for (String bs : receiveFuncs) {
				StringUtil.stringToByteBuffer(1, bs, bb);
			}
		}
		String[] remote = this.getRemote();
		if(remote==null) {
			bb.put((byte)0);
		}else {
			bb.put((byte)remote.length);
			for (String bs : remote) {
				StringUtil.stringToByteBuffer(2, bs, bb);
			}
		}
		bb.putInt(this.getSalt());
		bb.putLong(this.getRemoteSentTime());
		byte[] sign = this.getSign();
		if(sign==null) {
			bb.putShort((short)0);
		}else {
			bb.putShort((short)sign.length);
			bb.put(sign);
		}
	}
	
	public byte[] getPubKey() {
		return pubKey;
	}
	public String[] getFuncs() {
		return receiveFuncs;
	}
	public String[] getRemote() {
		return remote;
	}
	public int getSalt() {
		return salt;
	}
	public long getRemoteSentTime() {
		return remoteSentTime;
	}
	public byte[] getSign() {
		return sign;
	}
	@Override
	public FuncProvider getFunc() { 
		throw new RuntimeException("Node not need getFunc");
	}

	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("NODE$").append(Integer.toHexString(hashCode())).append("[key:").append(Pblog.byteArray2Hex(getPubKey()));
		sb.append(",func:").append(Arrays.toString(getFuncs())).append(",remote:").append(Arrays.toString(getRemote())).append(",");
		sb.append(sign==null?"unSign":"Signed").append(",");
		sb.append(System.currentTimeMillis()-getRemoteSentTime()).append("ms ago]");
		return sb.toString();
	}
}
